﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.MemoryMappedFiles;

namespace Common.Protocol
{
    public static class Reader
    {
        public static int ProtocolBufferSizeMb = 64;
        public static long ProtocolBufferSize = ProtocolBufferSizeMb * 1048576;

        private static string filePath;

        private static MemoryMappedFile file;
        private static MemoryMappedViewStream stream;
        
        public static List<BlockHeader> BlockHeaders { get; private set; }

        public static List<Int32> HeadersOffsets = new List<Int32>();

        public static void Open(string filePath)
        {
            Reader.filePath = filePath;
            Reader.file = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open);
            Reader.stream = Reader.file.CreateViewStream();
            Reader.BlockHeaders = new List<BlockHeader>();
            Reader.ReadHeaders();
        }

        private static void ReadHeaders()
        {
            byte[] buffer = new byte[3000];
            try
            {
                int offset = 0;
                Reader.stream.Read(buffer, 0, 3000);
                Int32 magic = BitConverter.ToInt32(buffer, 0);
                if(magic == -129)
                {
                    Reader.HeadersOffsets.Add(offset);
                    BlockHeader header = new BlockHeader();
                    header.ParseFromBinary(buffer, 0);
                    Reader.BlockHeaders.Add(header);

                    int? firstBlockId = null;

                    do
                    {
                        offset = header.HeaderPrev;
                        Reader.stream.Position = offset;
                        Reader.stream.Read(buffer, 0, 3000);
                        magic = BitConverter.ToInt32(buffer, 0);
                        if (magic == -129)
                        {
                            Reader.HeadersOffsets.Insert(1, offset);
                            header = new BlockHeader();
                            header.ParseFromBinary(buffer, 0);
                            Reader.BlockHeaders.Insert(1, header);
                            if (firstBlockId == null)
                            {
                                firstBlockId = header.BlockId;
                            }
                        }
                    }
                    while (header.BlockId != firstBlockId);

                    Reader.BlockHeaders.RemoveAt(1);
                }
            }
            catch(Exception e)
            {   
            }
        }

        public static void Close()
        {
            Reader.stream.Close();
            Reader.stream.Dispose();
            Reader.file.Dispose();
        }

        // повертає -1, якщо запису за вказаний час нема
        // повертає 0, якщо у вказаний час був розрив каналу
        public static float GetChannelValue(int channelId, DateTime time, int headerNumber)
        {
            //// after thie "while" i == number of block to read from
            //int headerNumber = 0;
            //while (Reader.BlockHeaders[headerNumber].TimeMs < (Reader.BlockHeaders[headerNumber].Start - time).TotalMilliseconds)
            //{
            //    if (headerNumber == Reader.BlockHeaders.Count)
            //    {
            //        return -1;
            //    }
            //}

            BlockHeader blockHeader = null;

            foreach (var header in BlockHeaders)
            {
                if (header.BlockId == headerNumber)
                {
                    blockHeader = header;
                    break;
                }
            }

            long virtualOffset = blockHeader.DataOffset;
            long milliseconds = (long)(time - blockHeader.Start).TotalMilliseconds;
            long sum = 0;
            for (int j = 0; j < blockHeader.Channels.Count; j++)
            {
                if (j < channelId)
                {
                    sum += milliseconds / blockHeader.Channels[j].RegPeriod + 1;
                }
                else
                {
                    sum += milliseconds / blockHeader.Channels[j].RegPeriod;
                }
            }

            virtualOffset += 2 * sum;

            long realOffset = virtualOffset % Reader.ProtocolBufferSize + 3000;

            // reading data from block with number i
            byte[] data = new byte[2];
            Reader.stream.Position = realOffset;
            Reader.stream.Read(data, 0, 2);
            UInt16 val = BitConverter.ToUInt16(data, 0);
            if (val >= 32768)
            {
                val -= 32768;
            }

            val -= 3750;
            return (val / 25000f) * blockHeader.Channels[channelId].ValueMax
                + blockHeader.Channels[channelId].ValueMin;
        }
    }
}